package cn.apthink.schedule;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import cn.apthink.utility.DateUtil;
import cn.apthink.utility.Util;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class Plan {
	private String id;
	private String departmentId;
	private LocalDateTime startTime;
	private LocalDateTime endTime;
	private Integer worker;
	// 手工效率（计划员排产设置）
	private Double manualEfficiency;
	// 0-未锁定 1-锁定
	private boolean locked;
	// 计划明细（生产单明细及其预设生产效率）
	private List<PlanDetail> details;
	// 计划有效时长（排产时计算保存）
	private long duration;
	// 优化后得分
	private long score;

	public Plan(String id, String departmentId, String startTime, String endTime) {
		this.id = id;
		this.departmentId = departmentId;
		this.startTime = DateUtil.toDateTime(startTime);
		this.endTime = DateUtil.toDateTime(endTime);
	}

	public int planAmount() {
		if (!Util.isEmpty(details)) {
			return details.stream().map(PlanDetail::getPlanAmount).reduce(Integer::sum).get();
		}
		return 0;
	}

	/**
	 * 计划负载=sum(planamount*jobsam)
	 * 
	 * @return
	 */
	public double loadding() {
		if (!Util.isEmpty(details)) {
			return details.stream().map(PlanDetail::loadding).reduce(Double::sum).get();
		}
		return 0;
	}

	/**
	 * 计划总时长，需考虑效率（有计划指定效率时替换预设生产效率）
	 * 
	 * @return
	 */
	public long loaddingByEfficiency() {
		if (!Util.isEmpty(details)) {
			if (Util.isEmpty(manualEfficiency) || manualEfficiency == 0)
				return Math.round(details.stream().map(PlanDetail::loaddingByEfficiency).reduce(Double::sum).get());
			else
				return Math.round(details.stream().map(PlanDetail::loadding).reduce(Double::sum).get() / manualEfficiency);
		} else {
			return 0;
		}
	}

	/**
	 * 计划下生产单最早开工期
	 * 
	 * @return
	 */
	public LocalDate earliestStartDate() {
		if (Util.isEmpty(details))
			return null;
		else
			return details.stream().map(PlanDetail::getEarliestStartDate).max((a, b) -> a.compareTo(b)).get();
	}

	/**
	 * 计划下生产单最晚完工期
	 * 
	 * @return
	 */
	public LocalDate latestEndDate() {
		if (Util.isEmpty(details))
			return null;
		else
			return details.stream().map(PlanDetail::getLatestEndDate).min((a, b) -> a.compareTo(b)).get();
	}

	/**
	 * 计划下生产单最大效率值
	 * 
	 * @return
	 */
	public double maxEfficiency() {
		if (Util.isEmpty(details))
			return 0.0;
		else
			return details.stream().map(PlanDetail::getEfficiency).max((a, b) -> a.compareTo(b)).get();
	}

	public String getEfficiencys() {
		if (Util.isEmpty(details))
			return null;
		else {
			List<String> lst = details.stream().map(x -> {
				return String.valueOf(x.getEfficiency());
			}).distinct().collect(Collectors.toList());
			return Util.getString(lst);
		}
	}

	/**
	 * 计划下生产单工时
	 * 
	 * @return
	 */
	public String getSams() {
		if (Util.isEmpty(details))
			return null;
		else {
			List<String> lst = details.stream().map(x -> {
				return String.valueOf(x.getSam());
			}).distinct().collect(Collectors.toList());
			return Util.getString(lst);
		}
	}

	/**
	 * 计划下生产单的款式
	 * 
	 * @return
	 */
	public String getStyleId() {
		if (Util.isEmpty(details))
			return null;
		else
			return details.get(0).getStyleId();
	}

	public String getProductId() {
		if (Util.isEmpty(details))
			return null;
		else
			return details.get(0).getProductId();
	}

	/**
	 * 计划下产品的数量
	 * 
	 * @return
	 */
	public Map<String, Integer> getProductAmount() {
		Map<String, Integer> result = new HashMap<String, Integer>();
		details.forEach(x -> {
			String id = x.getProductId();
			if (result.containsKey(id)) {
				result.put(id, (result.get(id) + x.getPlanAmount()));
			} else {
				result.put(id, x.getPlanAmount());
			}
		});
		return result;
	}

	public List<PlanDetail> getDetailByProduct(String productId) {
		return details.stream().filter(x -> {
			return productId.equals(x.getProductId());
		}).collect(Collectors.toList());
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Plan other = (Plan) obj;
		return Objects.equals(id, other.id);
	}

	@Override
	public int hashCode() {
		return Objects.hash(id);
	}

	@Override
	public String toString() {
		return "[" + id + "," + getDepartmentId() + "," + getScore() + "(" + getProductId() + "," + getSams() + ","
				+ getEfficiencys() + ")" + "(" + startTime + "," + endTime + "," + getDuration() + ")" + "]";
	}

	public static Plan testData(String planId, String lineId) {
		Plan p = new Plan(planId, lineId, "2025-01-02 08:00:00", "2025-01-07 11:02:58");
		List<PlanDetail> pds = new ArrayList<PlanDetail>();
		PlanDetail pd = new PlanDetail("pd11", "j11", 20, 20, 1080, 0.7);
		pd.setEarliestStartDate(DateUtil.toDate("2025-01-20"));
		pd.setLatestEndDate(DateUtil.toDate("2025-01-27"));
		pd.setProductId("p1");
		pds.add(pd);
		pd = new PlanDetail("pd12", "j12", 50, 50, 1085, 0.7);
		pd.setEarliestStartDate(DateUtil.toDate("2025-01-19"));
		pd.setLatestEndDate(DateUtil.toDate("2025-01-25"));
		pd.setProductId("p1");
		pds.add(pd);
		p.setDetails(pds);
		return p;
	}

	public static Plan testData1(String planId, String lineId, String productId, int amount, String earliestStartDate,
			String latestEndDate, String startTime, String endTime) {
		Plan p = new Plan(planId, lineId, startTime, endTime);
		List<PlanDetail> pds = new ArrayList<PlanDetail>();
		PlanDetail pd = new PlanDetail("pd21", "j21", amount, amount, 1080, 0.8);
		pd.setEarliestStartDate(DateUtil.toDate(earliestStartDate));
		pd.setLatestEndDate(DateUtil.toDate(latestEndDate));
		pd.setProductId(productId);
		pds.add(pd);
		p.setDetails(pds);
		return p;
	}

	public static Plan testData2(String planId, String lineId, String startDate, String endDate, String startTime, String endTime) {
		Plan p = new Plan(planId, lineId, startTime, endTime);
		List<PlanDetail> pds = new ArrayList<PlanDetail>();
		PlanDetail pd = new PlanDetail("pd31", "j31", 50, 50, 1080, 0.7);
		pd.setEarliestStartDate(DateUtil.toDate(startDate));
		pd.setLatestEndDate(DateUtil.toDate(endDate));
		pd.setProductId("p1");
		pds.add(pd);
		pd = new PlanDetail("pd32", "j32", 50, 50, 1085, 0.75);
		pd.setEarliestStartDate(DateUtil.toDate(startDate));
		pd.setLatestEndDate(DateUtil.toDate(endDate));
		pd.setProductId("p2");
		pds.add(pd);
		p.setDetails(pds);
		return p;
	}

	public static List<Plan> testDatas(String name) {
		List<Plan> result = new ArrayList<Plan>();
		result.add(testData(name + "p1", name));
		result.add(testData1(name + "p2", name, "p2", 60, "2025-01-07", "2025-01-20", "2025-01-07 16:00:00", "2025-01-08 10:00:00"));
		result.add(testData2(name + "p3", name, "2025-01-15", "2025-01-25", "2025-01-10 16:00:00", "2025-01-15 10:00:00"));
		return result;
	}

}
